﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace PowerSwitch
{
    internal class PowerSettingNotificationMsgFilter : IDisposable
    {
        // POWER PERSONALITIES

        // Maximum Power Savings - Very aggressive power savings 
        // measures are used to help stretch battery life.
        public static Guid GUID_MAX_POWER_SAVINGS = new Guid(0xA1841308, 0x3541, 0x4FAB, 0xBC, 0x81, 0xF7, 0x15, 0x56, 0xF2, 0x0B, 0x4A);
        // No Power Savings - Almost no power savings measures are used.
        public static Guid GUID_MIN_POWER_SAVINGS = new Guid(0x8C5E7FDA, 0xE8BF, 0x4A96, 0x9A, 0x85, 0xA6, 0xE2, 0x3A, 0x8C, 0x63, 0x5C);
        // Typical Power Savings - Fairly aggressive power savings measures are used.
        public static Guid GUID_TYPICAL_POWER_SAVINGS = new Guid(0x381B4222, 0xF694, 0x41F0, 0x96, 0x85, 0xFF, 0x5B, 0xB2, 0x60, 0xDF, 0x2E);

        public event Action<Guid> OnPowerSchemeChanged;
        PowerSettingNotificationHelper pwrNotHelper;
        bool init;

        public PowerSettingNotificationMsgFilter(Form form)
        {
            if (!form.IsHandleCreated)
                throw new InvalidOperationException("the form handle is not yet created");
            pwrNotHelper = new PowerSettingNotificationHelper(form.Handle);
            pwrNotHelper.OnPowerSchemeChanged += new Action<Guid>(pwrNotHelper_OnPowerSchemeChanged);
            init = false;
            Init();
        }

        void pwrNotHelper_OnPowerSchemeChanged(Guid obj)
        {
            if (OnPowerSchemeChanged != null)
                OnPowerSchemeChanged(obj);
        }

        //I used the same method name as IMessageFilter
        public bool PreFilterMessage(ref Message m)
        {
            if(init)
                return pwrNotHelper.PreFilterMessage(ref m);
            return false;
        }

        private void Init()
        {
            if (!init)
            {
                pwrNotHelper.RegisterForPowerNotifications();
                init = true;
            }
        }

        
        public void Destroy()
        {
            if (init)
            {
                pwrNotHelper.UnregisterForPowerNotifications();
                init = false;
            }
        }
        
        void IDisposable.Dispose()
        {
            Destroy();
        }

        class PowerSettingNotificationHelper
        {

            private IntPtr hPowerScheme;
            public event Action<Guid> OnPowerSchemeChanged;
            IntPtr handle;

            #region pinvoke
            // These Guids can be found in WinNT.h in the Vista Platform SDK

            Guid GUID_BATTERY_PERCENTAGE_REMAINING = new Guid("A7AD8041-B45A-4CAE-87A3-EECBB468A9E1");
            Guid GUID_MONITOR_POWER_ON = new Guid(0x02731015, 0x4510, 0x4526, 0x99, 0xE6, 0xE5, 0xA1, 0x7E, 0xBD, 0x1A, 0xEA);
            Guid GUID_ACDC_POWER_SOURCE = new Guid(0x5D3E9A59, 0xE9D5, 0x4B00, 0xA6, 0xBD, 0xFF, 0x34, 0xFF, 0x51, 0x65, 0x48);
            Guid GUID_POWERSCHEME_PERSONALITY = new Guid(0x245D8541, 0x3943, 0x4422, 0xB0, 0x25, 0x13, 0xA7, 0x84, 0xF6, 0x79, 0xB7);

         


            // Win32 decls and defs
            //
            const int WM_POWERBROADCAST = 0x0218;

            const int PBT_APMQUERYSUSPEND = 0x0000;
            const int PBT_APMQUERYSTANDBY = 0x0001;
            const int PBT_APMQUERYSUSPENDFAILED = 0x0002;
            const int PBT_APMQUERYSTANDBYFAILED = 0x0003;
            const int PBT_APMSUSPEND = 0x0004;
            const int PBT_APMSTANDBY = 0x0005;
            const int PBT_APMRESUMECRITICAL = 0x0006;
            const int PBT_APMRESUMESUSPEND = 0x0007;
            const int PBT_APMRESUMESTANDBY = 0x0008;
            const int PBT_APMBATTERYLOW = 0x0009;
            const int PBT_APMPOWERSTATUSCHANGE = 0x000A; // power status
            const int PBT_APMOEMEVENT = 0x000B;
            const int PBT_APMRESUMEAUTOMATIC = 0x0012;
            const int PBT_POWERSETTINGCHANGE = 0x8013; // DPPE


            const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
            const int DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001;

            // This structure is sent when the PBT_POWERSETTINGSCHANGE message is sent.
            // It describes the power setting that has changed and contains data about the change
            [StructLayout(LayoutKind.Sequential, Pack = 4)]
            internal struct POWERBROADCAST_SETTING
            {
                public Guid PowerSetting;
                public Int32 DataLength;
            }

            [DllImport(@"User32", EntryPoint = "RegisterPowerSettingNotification",
                 CallingConvention = CallingConvention.StdCall)]
            private static extern IntPtr RegisterPowerSettingNotification(
                IntPtr hRecipient,
                ref Guid PowerSettingGuid,
                Int32 Flags);

            [DllImport(@"User32", EntryPoint = "UnregisterPowerSettingNotification",
                 CallingConvention = CallingConvention.StdCall)]
            private static extern bool UnregisterPowerSettingNotification(
                IntPtr handle);
           

            // Used by SetThreadExecutionState.
            [FlagsAttribute]
            public enum EXECUTION_STATE : uint
            {
                ES_SYSTEM_REQUIRED = 0x00000001,
                ES_DISPLAY_REQUIRED = 0x00000002,
                // Legacy flag, should not be used.
                // ES_USER_PRESENT   = 0x00000004,
                ES_CONTINUOUS = 0x80000000,
            }

            [DllImport("Kernel32.DLL", CharSet = CharSet.Auto,
                 SetLastError = true)]
            public extern static
                EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE state);

            #endregion

            public PowerSettingNotificationHelper(IntPtr FormHandle)
            {
                handle = FormHandle;
            }

            public void RegisterForPowerNotifications()
            {
                hPowerScheme = RegisterPowerSettingNotification(this.handle,
                                            ref GUID_POWERSCHEME_PERSONALITY,
                                            DEVICE_NOTIFY_WINDOW_HANDLE);
            }

            public void UnregisterForPowerNotifications()
            {
                UnregisterPowerSettingNotification(hPowerScheme);
            }

            public bool PreFilterMessage(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_POWERBROADCAST:
                        OnPowerBroadcast(ref m);
                        return true;
                        //break;

                }
                return false;
            }

            private void OnPowerBroadcast(ref Message m)
            {
                if ((int)m.WParam == PBT_POWERSETTINGCHANGE)
                {
                    // Respond to a change in the power settings.
                    PowerSettingChange(ref m);
                }
            }

            private void PowerSettingChange(ref Message m)
            {
                POWERBROADCAST_SETTING ps =
                                    (POWERBROADCAST_SETTING)Marshal.PtrToStructure(
                                                    m.LParam, typeof(POWERBROADCAST_SETTING));

                IntPtr pData = (IntPtr)((int)m.LParam + Marshal.SizeOf(ps));

                // Handle a change to the power plan.
                if (ps.PowerSetting == GUID_POWERSCHEME_PERSONALITY)
                {
                    UpdateSelectedPowerPlan(ps, pData);
                }
            }

            private void UpdateSelectedPowerPlan(POWERBROADCAST_SETTING ps, IntPtr pData)
            {
                if (ps.DataLength == Marshal.SizeOf(typeof(Guid)))
                {
                    Guid newPersonality = (Guid)Marshal.PtrToStructure(pData, typeof(Guid));
                    if (OnPowerSchemeChanged!=null)
                        OnPowerSchemeChanged(newPersonality);

                    /*
                    if (newPersonality == GUID_MAX_POWER_SAVINGS)
                    {
                   
                    }
                    else if (newPersonality == GUID_MIN_POWER_SAVINGS)
                    {
                    
                    }
                    else if (newPersonality == GUID_TYPICAL_POWER_SAVINGS)
                    {
                    
                    }
                    else
                    {
                    
                    }
                    */
                }

            }

            

        }
    }
}
